在开发中实现动画的方法有很多,不管是 react
还是 vue
都有开源的动画组件库来更加方便的动画实现效果
react 动画组件库是 React-Transition-group
,在说这个组件库之前,我们先来看下借助 CSS3 实现最基本的动画样式
CSS3 实现动画样式
constructor(props) {
super(props)
this.state = {
show: true
}
this.handleToggle = this.handleToggle.bind(this)
}
render() {
return (
<Fragment>
<div className={this.state.show ? 'show' : 'hide'}>hello</div>
<button onClick={this.handleToggle}>toggle</button>
</Fragment>
);
}
handleToggle() {
this.setState({
show: this.state.show ? false : true
})
}
.show {
opacity: 1;
transition: all 1s ease-in;
}
.hide {
opacity: 0;
transition: all 1s ease-in;
}
这样在浏览器中通过控制 toggle,就可以看到 hello 若隐若现了,就借助了 CSS3 实现了最基本的动画样式
使用 react-transition-group 实现动画
在 github 中搜索 react-transition-group
,可以看到 star 最多的项目,就是 react-transition-group
先安装依赖 npm install react-transition-group --save
然后在项目中引入 import { CSSTransition } from 'react-transition-group'
可以看到官网的demo
classNames="fade" applies fade-enter, fade-enter-active, fade-enter-done, fade-exit, fade-exit-active, fade-exit-done, fade-appear, and fade-appear-active
.
Transition
过渡组件 ( Transiton
) 允许您用一个简单的声明性 API 描述随着时间的推移从一个组件状态到另一个组件状态的转换
默认展示组件某个特定状态的样式,而不是创建渐变动画
<Transition
in={this.state.show}
timeout={1000}
// unmountOnExit
>
{state=>{
if(state === 'entering' || state === 'entered')
return <div className="on">{state}</div>
else
return <div className="off">{state}</div>
}}
</Transition>
<button onClick={this.handleAddItem}>toggle {`${this.state.show}`}</button>
Transition
中间传入一个函数,也就是属性 children ,并获得一个参数 state,
state 包含了内部组件的 transition
状态,分别有
entering
entered
exiting
exited
unmounted
上述 🌰 的意思可以理解为
你设置的时间是 1000ms,当 in 从 false 变成 true 的时候, 显示 <div className="on">{state}</div>
( entering
和 entered
的状态 ), 相反的,当 in 从 true 变成 false 的时候,1000ms 之后, 切换到 <div className="off">{state}</div>
( exiting
和 exited
的状态 )
运行原理
从头来看,它其实就是一个状态机,跟动画没什么关系,所以它的名字也是叫 Transition
有点像路由,在不同的组件中选择一个渲染,唯一的区别是,它只有4个路由选项:
进入 ( in === true )时,url 是 entering
,1000ms 后,url 变成 entered
退出 ( in ===false ) 时,url 是 exiting
,1000ms 后,url 变成 exited
然后加上 css ease-in-out
就成了动画
CSSTransition
展示组件从状态到另一个状态的动态变化,需要定义 className 和相关样式
最常用的是用来动画一个组件的安装和卸载,但也可以用来描述在适当的过渡状态
可以将我们之前用 CSS3 写的样式修改为
render() {
return (
<Fragment>
<CSSTransition
in={this.state.show}
timeout={1000}
// 前缀名注意S
classNames='fade'
>
<div>hello</div>
</CSSTransition>
<button onClick={this.handleToggle}>toggle</button>
</Fragment>
)
}
handleToggle() {
this.setState({
show: this.state.show ? false : true
})
}
.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
transition: opacity 1s ease-in;
}
/* 入场动画执行完毕后,保持状态 */
.fade-enter-done {
opacity: 1;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 1s ease-in;
}
.fade-exit-done {
opacity: 0;
}
这样就可以实现和之前相同的动画效果了
咋一看虽然稍微复杂了点,但是它可以带给我们很多新的特效
比如参数 in
是 true
or false
,代表了是’淡入’状态,还是’淡出’状态
timeout
代表了整个的持续时间
unmountOnExit
属性,添加到代码中,会发现当我们点隐藏的时候,对应的 DOM 被移出了,点显示的时候,DOM 又出来了
借助 react-transition-group
这个库,实现起来非常简单
继续看它的文档,这个库提供了很多钩子函数
假设当这个 hello 显示出来之后,希望它的颜色能变成红色,现在实现就变得很简单了
只需要在入场动画结束之后,将 color 变成 red
.fade-enter-done {
opacity: 1;
color: red
}
还可以用 js 的方式来实现,怎么做呢?
在 CSSTransition
组件中添加一个钩子onEntered
<CSSTransition
in={this.state.show}
timeout={1000}
classNames='fade'
unmountOnExit
onEntered={(el) => {el.style.color='blue'}}
>
<div>hello</div>
</CSSTransition>
钩子和生命周期函数是一个东西,就是在某个时刻会自动执行的函数
onEntered
钩子什么时候会自动执行呢?就是当入场动画结束之后,就会被执行
el
就是指的内部的 div
元素
如果希望第一次展示的时候也有动画效果,应该怎么办呢?
同样也需要在 CSSTransition
组件中添加一个 appear={true}
,同时在入场动画的第一帧添加 fade-appear
,同时在入场动画的第二帧以及整个过程中添加 fade-appear-active
/* enter是入场前的刹那(点击按钮),appear指页面第一次加载前的一刹那(自动) */
.fade-enter, .fade-appear {
opacity: 0;
}
/* enter-active指入场后到入场结束的过程,appear-active则是页面第一次加载自动执行 */
.fade-enter-active, .fade-appear-active {
opacity: 1;
transition: opacity 1s ease-in;
}
这些也就是 react-transition-group
这个库比较核心的内容,其他更复杂的方法可以查阅 Transition
TransitionGroup
如果要做多个元素的动画切换呢?
这个时候就要用到 TransitionGroup
TransitionGroup
实际上就是实现多个Transition 或者CSSTransition组合的效果
用来管理一些列组件的动画,例如列表
首先引入这个组件 import TransitionGroup from 'react-transition-group'
constructor(props) {
super(props)
this.state = {
data: []
}
this.handleAddItem = this.handleAddItem.bind(this)
}
render() {
return (
<Fragment>
<TransitionGroup>
{
this.state.data.map((item, index) => {
return (
<CSSTransition
timeout={1000}
classNames='fade'
unmountOnExit
onEntered={(el) => {el.style.color='blue'}}
appear={true}
key={index}
>
<div>{item}</div>
</CSSTransition>
)
})
}
</TransitionGroup>
<button onClick={this.handleAddItem}>toggle</button>
</Fragment>
)
}
handleAddItem() {
this.setState((prevState) => {
return {
data: [...prevState.data, 'item']
}
})
}
这样配合 TransitionGroup
和 CSSTransition
就可以进行多个元素或者组件切换这样的动画效果了